[hvm] Keep track of time offset between domain time and dom0 time.
authorChristian Limpach <Christian.Limpach@xensource.com>
Tue, 3 Apr 2007 12:22:37 +0000 (13:22 +0100)
committerChristian Limpach <Christian.Limpach@xensource.com>
Tue, 3 Apr 2007 12:22:37 +0000 (13:22 +0100)
On each rtc time update from the hvm domain, we send an ioreq request
to qemu which then updates the time offset xenstore entry.  The time
offset is preserved across reboot and can be set on domain creation
by setting the rtc_timeoffset variable in a config file.

From: Andrei Petrov <andrei.petrov@xensource.com>
Signed-off-by: Christian Limpach <Christian.Limpach@xensource.com>
16 files changed:
tools/examples/xmexample.hvm
tools/ioemu/target-i386-dm/helper2.c
tools/ioemu/vl.c
tools/ioemu/vl.h
tools/ioemu/xenstore.c
tools/python/xen/xend/XendConfig.py
tools/python/xen/xend/XendDomainInfo.py
tools/python/xen/xend/image.py
tools/python/xen/xm/create.py
xen/arch/x86/hvm/intercept.c
xen/arch/x86/hvm/platform.c
xen/arch/x86/hvm/rtc.c
xen/arch/x86/time.c
xen/include/asm-x86/hvm/io.h
xen/include/asm-x86/time.h
xen/include/public/hvm/ioreq.h

index 33ce3203d341e61539c888873dd1135ec964a37e..06662a0d17baee7314d9adf61cbc519e24164b03 100644 (file)
@@ -179,6 +179,10 @@ serial='pty'
 #localtime=1
 
 
+#-----------------------------------------------------------------------------
+#    set the real time clock offset in seconds [default=0 i.e. same as dom0]
+#rtc_timeoffset=3600
+
 #-----------------------------------------------------------------------------
 #    start in full screen
 #full-screen=1   
index 17261e632d77d693a73508dd89c5348fd0e53da3..8b98e8a1840656c668d80930a4926a7b18170ec5 100644 (file)
@@ -74,6 +74,8 @@ int vcpus = 1;
 
 int xc_handle;
 
+long time_offset = 0;
+
 shared_iopage_t *shared_page = NULL;
 
 #define BUFFER_IO_MAX_DELAY  100
@@ -439,6 +441,34 @@ void cpu_ioreq_xor(CPUState *env, ioreq_t *req)
     req->data = tmp1;
 }
 
+void timeoffset_get()
+{
+    char *p;
+
+    p = xenstore_vm_read(domid, "rtc/timeoffset", NULL);
+    if (!p)
+       return;
+
+    if (sscanf(p, "%ld", &time_offset) == 1)
+       fprintf(logfile, "Time offset set %ld\n", time_offset);
+    else
+       time_offset = 0;
+
+    xc_domain_set_time_offset(xc_handle, domid, time_offset);
+
+    free(p);
+}
+
+void cpu_ioreq_timeoffset(CPUState *env, ioreq_t *req)
+{
+    char b[64];
+
+    time_offset += (ulong)req->data;
+
+    sprintf(b, "%ld", time_offset);
+    xenstore_vm_write(domid, "rtc/timeoffset", b);
+}
+
 void cpu_ioreq_xchg(CPUState *env, ioreq_t *req)
 {
     unsigned long tmp1;
@@ -478,6 +508,9 @@ void __handle_ioreq(CPUState *env, ioreq_t *req)
     case IOREQ_TYPE_XCHG:
         cpu_ioreq_xchg(env, req);
         break;
+    case IOREQ_TYPE_TIMEOFFSET:
+       cpu_ioreq_timeoffset(env, req);
+       break;
     default:
         hw_error("Invalid ioreq type 0x%x\n", req->type);
     }
index 66174de66f5a8fff76325eba588cc428ef74ab46..4ec5e0817d74eeea9a106bbbb6905fcc152dc3e0 100644 (file)
@@ -6670,6 +6670,9 @@ int main(int argc, char **argv)
     }
     free(page_array);
 #endif
+
+    timeoffset_get();
+
 #else  /* !CONFIG_DM */
 
     phys_ram_base = qemu_vmalloc(phys_ram_size);
index a6b53aa3125744e8febce3993783a3c89e89449e..4ef250f766e583a68f6f828ccdfce8fc770cb5ad 100644 (file)
@@ -1276,6 +1276,12 @@ int xenstore_unsubscribe_from_hotplug_status(struct xs_handle *handle,
                                              const char *inst,
                                              const char *token);
 
+int xenstore_vm_write(int domid, char *key, char *val);
+char *xenstore_vm_read(int domid, char *key, int *len);
+
+/* helper2.c */
+extern long time_offset;
+void timeoffset_get(void);
 
 /* xen_platform.c */
 void pci_xen_platform_init(PCIBus *bus);
index 26063f5a9395eb982d2a5b58e1fba1c463fc6c62..77819cea67247190ba3f2bfb74db4f6f2a70afad 100644 (file)
@@ -567,3 +567,72 @@ int xenstore_unsubscribe_from_hotplug_status(struct xs_handle *handle,
 
     return rc;
 }
+
+char *xenstore_vm_read(int domid, char *key, int *len)
+{
+    char *buf = NULL, *path = NULL, *value = NULL;
+
+    if (xsh == NULL)
+       goto out;
+
+    path = xs_get_domain_path(xsh, domid);
+    if (path == NULL) {
+       fprintf(logfile, "xs_get_domain_path(%d): error\n", domid);
+       goto out;
+    }
+
+    pasprintf(&buf, "%s/vm", path);
+    free(path);
+    path = xs_read(xsh, XBT_NULL, buf, NULL);
+    if (path == NULL) {
+       fprintf(logfile, "xs_read(%s): read error\n", buf);
+       goto out;
+    }
+
+    pasprintf(&buf, "%s/%s", path, key);
+    value = xs_read(xsh, XBT_NULL, buf, len);
+    if (value == NULL) {
+       fprintf(logfile, "xs_read(%s): read error\n", buf);
+       goto out;
+    }
+
+ out:
+    free(path);
+    free(buf);
+    return value;
+}
+
+int xenstore_vm_write(int domid, char *key, char *value)
+{
+    char *buf = NULL, *path = NULL;
+    int rc = -1;
+
+    if (xsh == NULL)
+       goto out;
+
+    path = xs_get_domain_path(xsh, domid);
+    if (path == NULL) {
+       fprintf(logfile, "xs_get_domain_path(%d): error\n");
+       goto out;
+    }
+
+    pasprintf(&buf, "%s/vm", path);
+    free(path);
+    path = xs_read(xsh, XBT_NULL, buf, NULL);
+    if (path == NULL) {
+       fprintf(logfile, "xs_read(%s): read error\n", buf);
+       goto out;
+    }
+
+    pasprintf(&buf, "%s/%s", path, key);
+    rc = xs_write(xsh, XBT_NULL, buf, value, strlen(value));
+    if (rc) {
+       fprintf(logfile, "xs_write(%s, %s): write error\n", buf, key);
+       goto out;
+    }
+
+ out:
+    free(path);
+    free(buf);
+    return rc;
+}
index e36e89a0dfb347997653e5d9a1b2fb7347956354..a4a9d43fe591d0e505e896bd6ae2a1d6e7951a3d 100644 (file)
@@ -118,7 +118,7 @@ LEGACY_CFG_TO_XENAPI_CFG = reverse_dict(XENAPI_CFG_TO_LEGACY_CFG)
 # Platform configuration keys.
 XENAPI_PLATFORM_CFG = [ 'acpi', 'apic', 'boot', 'device_model', 'display', 
                         'fda', 'fdb', 'keymap', 'isa', 'localtime',
-                        'nographic', 'pae', 'serial', 'sdl',
+                        'nographic', 'pae', 'rtc_timeoffset', 'serial', 'sdl',
                         'soundhw','stdvga', 'usb', 'usbdevice', 'vnc',
                         'vncconsole', 'vncdisplay', 'vnclisten',
                         'vncpasswd', 'vncunused', 'xauthority']
@@ -203,6 +203,7 @@ LEGACY_CFG_TYPES = {
     'on_xend_stop':  str,
     'on_xend_start': str,
     'online_vcpus':  int,
+    'rtc/timeoffset': str,
 }
 
 # Values that should be stored in xenstore's /vm/<uuid> that is used
index 7ba20fb5660e5daaa6879db870797fe96fce52cb..425776ce12ab45ae439bf76a4ddf8c7a148c86d9 100644 (file)
@@ -859,7 +859,8 @@ class XendDomainInfo:
         # Check whether values in the configuration have
         # changed in Xenstore.
         
-        cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash']
+        cfg_vm = ['name', 'on_poweroff', 'on_reboot', 'on_crash',
+                  'rtc/timeoffset']
         
         vm_details = self._readVMDetails([(k,XendConfig.LEGACY_CFG_TYPES[k])
                                            for k in cfg_vm])
@@ -888,6 +889,11 @@ class XendDomainInfo:
             self.info.update_with_image_sxp(sxp.from_string(image_sxp))
             changed = True
 
+        # Check if the rtc offset has changes
+        if vm_details.get("rtc/timeoffset", 0) != self.info["platform"].get("rtc_timeoffset", 0):
+            self.info["platform"]["rtc_timeoffset"] = vm_details.get("rtc/timeoffset", 0)
+            changed = True
         if changed:
             # Update the domain section of the store, as this contains some
             # parameters derived from the VM configuration.
index d9e850ccc9542b9db226c30aaafe3ebe84ca452f..835a5d8060e380e13c15aa8f80bfa68ec6bb54b0 100644 (file)
@@ -256,9 +256,12 @@ class HVMImageHandler(ImageHandler):
         self.xauthority = vmConfig['platform'].get('xauthority')
         self.vncconsole = vmConfig['platform'].get('vncconsole')
 
+        rtc_timeoffset = vmConfig['platform'].get('rtc_timeoffset')
+
         self.vm.storeVm(("image/dmargs", " ".join(self.dmargs)),
                         ("image/device-model", self.device_model),
                         ("image/display", self.display))
+        self.vm.storeVm(("rtc/timeoffset", rtc_timeoffset))
 
         self.pid = None
 
index 0ae94d83c448ef72a4b526bc5d4b523e89fc0a53..1411f9be89b8b7ea987c74d618206cd9bcb17ff4 100644 (file)
@@ -186,6 +186,10 @@ gopts.var('cpus', val='CPUS',
           fn=set_value, default=None,
           use="CPUS to run the domain on.")
 
+gopts.var('rtc_timeoffset', val='RTC_TIMEOFFSET',
+          fn=set_value, default="0",
+          use="Set RTC offset.")
+
 gopts.var('pae', val='PAE',
           fn=set_int, default=1,
           use="Disable or enable PAE of HVM domain.")
@@ -717,7 +721,7 @@ def configure_hvm(config_image, vals):
     args = [ 'device_model', 'pae', 'vcpus', 'boot', 'fda', 'fdb',
              'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'soundhw',
              'vnc', 'vncdisplay', 'vncunused', 'vncconsole', 'vnclisten',
-             'sdl', 'display', 'xauthority',
+             'sdl', 'display', 'xauthority', 'rtc_timeoffset',
              'acpi', 'apic', 'usb', 'usbdevice', 'keymap' ]
     for a in args:
         if a in vals.__dict__ and vals.__dict__[a] is not None:
index 3de58812c32b626acc9a0853d8f31d9445ec5771..fb8497a996f3a0bef4a0d90b360e3f2d6475a467 100644 (file)
@@ -155,28 +155,13 @@ static inline void hvm_mmio_access(struct vcpu *v,
     }
 }
 
-int hvm_buffered_io_intercept(ioreq_t *p)
+int hvm_buffered_io_send(ioreq_t *p)
 {
     struct vcpu *v = current;
     spinlock_t  *buffered_io_lock;
     buffered_iopage_t *buffered_iopage =
         (buffered_iopage_t *)(v->domain->arch.hvm_domain.buffered_io_va);
     unsigned long tmp_write_pointer = 0;
-    int i;
-
-    /* ignore READ ioreq_t! */
-    if ( p->dir == IOREQ_READ )
-        return 0;
-
-    for ( i = 0; i < HVM_BUFFERED_IO_RANGE_NR; i++ ) {
-        if ( p->addr >= hvm_buffered_io_ranges[i]->start_addr &&
-             p->addr + p->size - 1 < hvm_buffered_io_ranges[i]->start_addr +
-                                     hvm_buffered_io_ranges[i]->length )
-            break;
-    }
-
-    if ( i == HVM_BUFFERED_IO_RANGE_NR )
-        return 0;
 
     buffered_io_lock = &v->domain->arch.hvm_domain.buffered_io_lock;
     spin_lock(buffered_io_lock);
@@ -205,6 +190,27 @@ int hvm_buffered_io_intercept(ioreq_t *p)
     return 1;
 }
 
+int hvm_buffered_io_intercept(ioreq_t *p)
+{
+    int i;
+
+    /* ignore READ ioreq_t! */
+    if ( p->dir == IOREQ_READ )
+        return 0;
+
+    for ( i = 0; i < HVM_BUFFERED_IO_RANGE_NR; i++ ) {
+        if ( p->addr >= hvm_buffered_io_ranges[i]->start_addr &&
+             p->addr + p->size - 1 < hvm_buffered_io_ranges[i]->start_addr +
+                                     hvm_buffered_io_ranges[i]->length )
+            break;
+    }
+
+    if ( i == HVM_BUFFERED_IO_RANGE_NR )
+        return 0;
+
+    return hvm_buffered_io_send(p);
+}
+
 int hvm_mmio_intercept(ioreq_t *p)
 {
     struct vcpu *v = current;
index 185a7c2f550abbda0440c583e15a6fe246e043d6..d8a751bd1d9eb185debbe8174792a457e0d6294d 100644 (file)
@@ -921,6 +921,26 @@ static void send_mmio_req(unsigned char type, unsigned long gpa,
     hvm_send_assist_req(v);
 }
 
+void send_timeoffset_req(unsigned long timeoff)
+{
+    ioreq_t p[1];
+
+    if ( timeoff == 0 )
+        return;
+
+    memset(p, 0, sizeof(*p));
+
+    p->type = IOREQ_TYPE_TIMEOFFSET;
+    p->size = 4;
+    p->dir = IOREQ_WRITE;
+    p->data = timeoff;
+
+    p->state = STATE_IOREQ_READY;
+
+    if ( !hvm_buffered_io_send(p) )
+        printk("Unsuccessful timeoffset update\n");
+}
+
 static void mmio_operands(int type, unsigned long gpa,
                           struct hvm_io_op *mmio_op,
                           unsigned char op_size)
index 036eba7bfecbfecf9e51f03d62bc57294c09e1ae..1b18dca204bd64722208073a12aaa3be3a256549 100644 (file)
@@ -157,6 +157,10 @@ static inline int from_bcd(RTCState *s, int a)
 static void rtc_set_time(RTCState *s)
 {
     struct tm *tm = &s->current_tm;
+    unsigned long before, after; /* XXX s_time_t */
+      
+    before = mktime(tm->tm_year, tm->tm_mon, tm->tm_mday,
+                   tm->tm_hour, tm->tm_min, tm->tm_sec);
     
     tm->tm_sec = from_bcd(s, s->hw.cmos_data[RTC_SECONDS]);
     tm->tm_min = from_bcd(s, s->hw.cmos_data[RTC_MINUTES]);
@@ -168,6 +172,10 @@ static void rtc_set_time(RTCState *s)
     tm->tm_mday = from_bcd(s, s->hw.cmos_data[RTC_DAY_OF_MONTH]);
     tm->tm_mon = from_bcd(s, s->hw.cmos_data[RTC_MONTH]) - 1;
     tm->tm_year = from_bcd(s, s->hw.cmos_data[RTC_YEAR]) + 100;
+
+    after = mktime(tm->tm_year, tm->tm_mon, tm->tm_mday,
+                   tm->tm_hour, tm->tm_min, tm->tm_sec);
+    send_timeoffset_req(after - before);
 }
 
 static void rtc_copy_date(RTCState *s)
index daba20ff391b2b867059599c4562aeb8d1a957bb..d31fa1798468280ff75d0649851b70c428e3d0bc 100644 (file)
@@ -573,7 +573,7 @@ static void init_platform_timer(void)
  * machines were long is 32-bit! (However, as time_t is signed, we
  * will already get problems at other places on 2038-01-19 03:14:08)
  */
-static inline unsigned long
+unsigned long
 mktime (unsigned int year, unsigned int mon,
         unsigned int day, unsigned int hour,
         unsigned int min, unsigned int sec)
index 092b037516df265a63480b3fe3c2272c9ba5eb98..cf46b0cb6d6cf5057d06a1bf1b5b623e0d93d39d 100644 (file)
@@ -127,6 +127,7 @@ static inline int hvm_portio_intercept(ioreq_t *p)
 }
 
 extern int hvm_mmio_intercept(ioreq_t *p);
+extern int hvm_buffered_io_send(ioreq_t *p);
 extern int hvm_buffered_io_intercept(ioreq_t *p);
 
 static inline int register_portio_handler(
@@ -145,6 +146,7 @@ static inline int irq_masked(unsigned long eflags)
 
 extern void send_pio_req(unsigned long port, unsigned long count, int size,
                          paddr_t value, int dir, int df, int value_is_ptr);
+void send_timeoffset_req(unsigned long timeoff);
 extern void handle_mmio(unsigned long gpa);
 extern void hvm_interrupt_post(struct vcpu *v, int vector, int type);
 extern void hvm_io_assist(struct vcpu *v);
index 731ce9500e3a4055643de6fe11f420337218246c..df74cf2962b80ea9cc72a220b4a8bf113a94ec5d 100644 (file)
@@ -16,4 +16,9 @@ static inline cycles_t get_cycles(void)
     return c;
 }
 
+unsigned long
+mktime (unsigned int year, unsigned int mon,
+        unsigned int day, unsigned int hour,
+        unsigned int min, unsigned int sec);
+
 #endif /* __X86_TIME_H__ */
index 4ff8936a5ac062d5605d9f223dec277db004889d..646246e854c837f3a5624e14ed33a077496cc4e1 100644 (file)
@@ -39,6 +39,7 @@
 #define IOREQ_TYPE_XOR          4
 #define IOREQ_TYPE_XCHG         5
 #define IOREQ_TYPE_ADD          6
+#define IOREQ_TYPE_TIMEOFFSET   7
 
 /*
  * VMExit dispatcher should cooperate with instruction decoder to